import { keywordCompare } from '@gnlab/utils'
import { useRefValue } from '@gnlab/utils/env-react'
import { mfrom } from '@gnlab/utils/rx'
import { OrderedMap } from 'immutable'
import { useStreams } from '@/lib/utils'
import services from '@/services'
import { type BaseGroup } from '@/services/contact-types'
import { type QywxInfo } from '@/services/qywxs'

/**
 * 企业微信数据
 */
export type QywxMap = OrderedMap<string, QywxInfo>

export function useQywxMap(input?: QywxMap) {
  const [defaultQywxMap] = useStreams(
    input ? mfrom(OrderedMap() as QywxMap) : services.qywxs.usables,
  )
  return input ?? defaultQywxMap
}

/**
 * 群数据
 */
export type GroupMap = OrderedMap<string, BaseGroup>
export type GroupFilter = (group: BaseGroup) => boolean

export function useGroups(
  // 指定参与选择的群（默认使用当前账号的群列表）
  // 若传入一个函数，会用这个函数过滤默认群列表作为参与选择的群
  input?: GroupMap | GroupFilter,

  // 由使用者自行维护的搜索条件（此 hooks 自带关键词搜索）
  // 关键词搜索、及此处指定的搜索条件，决定了当前展示哪些群（没展示出来的依然是参与选择行为的，例如之前如果选中了，会出现在已选中列表中）
  outerFilter?: GroupFilter,

  // 是否把不同联系人拥有的同一个群合并到一起
  combine = false,
) {
  const [defaultGroupMap] = useStreams(
    OrderedMap.isOrderedMap(input) ? mfrom(OrderedMap() as GroupMap) : services.groups.list,
  )
  const rawAllGroupMap =
    typeof input === 'function' ? defaultGroupMap.filter(input) : input ?? defaultGroupMap

  // 正常模式 key 是 group contact id，combine 模式的 key 是 conversationId
  const allGroupMap = combine ? combineGroups(rawAllGroupMap) : rawAllGroupMap

  const [search, setSearch] = useState('')
  const filteredGroups = useMemo(() => {
    const trimedSearch = search.trim()
    const filteredGroupMap =
      trimedSearch || outerFilter
        ? allGroupMap.filter(group => {
            if (trimedSearch && !keywordCompare(trimedSearch, group.name)) return false
            if (outerFilter && !outerFilter(group)) return false
            return true
          })
        : allGroupMap
    return [...filteredGroupMap.values()]
  }, [search, outerFilter, allGroupMap])

  return {
    allGroupMap,

    search,
    setSearch,
    filteredGroups,
  }
}

// 合并不同联系人所处的同一个群
function combineGroups(map: GroupMap) {
  let combined = OrderedMap<string, BaseGroup>()
  for (const group of map.values()) {
    if (!combined.has(group.conversationId)) {
      // 抹除企业微信信息，因为现在这个群已经不归单一某个企业微信了
      const updatedGroup = {
        ...group,
        id: group.conversationId, // 后续仍可直接读取群的 id，实际读的是 conversationId
        ownerWxid: '',
        isOwner: false, // 没有对应的企业微信关系，就不涉及企业微信是否是本群群主的情况
      }
      combined = combined.set(group.conversationId, updatedGroup)
    }
  }
  return combined
}

/**
 * 选中状态
 */
export type ConfirmChoosable = (group: BaseGroup, choosed: string[]) => boolean

export function useChoosed(params: {
  allGroupMap: GroupMap

  // 经过搜索条件过滤，当前展示给用户的群（用于进行全选/反选，因为这俩操作只针对当前展示给用户的群）
  filteredGroups: BaseGroup[]

  // 提供自定义逻辑来判断一个群是否可以被选择（默认所有群都可以被选择）
  // 注意：如果要根据“已选中的群”判断当前群是否可选中，必须使用参数传进来的 choosed，而不是之前 hooks 返回的 choosed，
  // 因为在“全选”过程中，之前 hooks 返回的 choosed 不会改变，但传进来的是最新的
  confirmChoosable?: ConfirmChoosable

  // 指定当前选中的群
  choosed: string[]

  setChoosed: (choosed: string[]) => void
}) {
  const { allGroupMap, filteredGroups, confirmChoosable, choosed, setChoosed } = params

  // 初始化时，检查一次 choosed，移除已不在 groupMap 里或不可选的群
  const choosedRef = useRefValue(choosed)
  useEffect(() => {
    const confirmed: string[] = []
    for (const id of choosedRef.current) {
      const group = allGroupMap.get(id)
      if (group && (!confirmChoosable || confirmChoosable(group, confirmed))) {
        confirmed.push(id)
      }
    }
    if (confirmed.length !== choosedRef.current.length) {
      setChoosed(confirmed)
    }
  }, [allGroupMap, choosedRef, confirmChoosable, setChoosed])

  // “经过过滤”且“可选”的群的 id
  const choosableFilteredIds = useMemo(() => {
    const choosableFilteredGroups = confirmChoosable
      ? filteredGroups.filter(g => confirmChoosable(g, choosed))
      : filteredGroups
    return new Set(choosableFilteredGroups.map(g => g.id))
  }, [filteredGroups, choosed, confirmChoosable])

  // 可选的群是否都被选中了  0: 全都没选  1: 全部选中  2: 部分选中
  const filteredChoosedAll = useMemo(() => {
    // 四种情况：
    // map []    inChoosed []
    // map [1,2] inChoosed []
    // map [1,2] inChoosed [1]
    // map [1,2] inChoosed [1,2]
    const inChoosed = choosed.filter(id => choosableFilteredIds.has(id)).length
    if (inChoosed === 0) return 0
    if (inChoosed === choosableFilteredIds.size) return 1
    return 2
  }, [choosed, choosableFilteredIds])

  const switchChoose = useCallback(
    (id: string) => {
      setChoosed(choosed.includes(id) ? choosed.filter(_id => _id !== id) : [...choosed, id])
    },
    [choosed, setChoosed],
  )

  const switchChooseAll = useCallback(() => {
    if (filteredChoosedAll === 1) {
      // 全不选
      setChoosed(choosed.filter(id => !choosableFilteredIds.has(id)))
    } else {
      // 全选
      let updated = choosed
      for (const group of filteredGroups) {
        if (
          !updated.includes(group.id) &&
          choosableFilteredIds.has(group.id) &&
          (!confirmChoosable || confirmChoosable(group, updated))
        ) {
          updated = [...updated, group.id]
        }
      }
      setChoosed(updated)
    }
  }, [
    filteredChoosedAll,
    filteredGroups,
    choosableFilteredIds,
    choosed,
    confirmChoosable,
    setChoosed,
  ])

  return {
    choosed,
    choosableFilteredIds,
    filteredChoosedAll,
    switchChoose,
    switchChooseAll,
  }
}
